from clang.cindex import CursorKind
from PyBirdViewCode.clang_utils.cfg_parser import (
    CFGManager,
    recursive_walk_non_control_flow_nodes,
)
from PyBirdViewCode.clang_utils import (
    parse_file,
    get_func_decl,
    iter_ast,
    get_node_start_pos,
)
from PyBirdViewCode.utils import FileManager
from typing import Tuple, List, TypedDict
import os
import subprocess  # Add subprocess module for executing shell commands

import re


def parse_preprocessor_file(file_path) -> Tuple[int, int, str, int, int]:
    """
    解析 .i 预处理文件，返回一个五元组列表：
    [(预处理后范围开始行，预处理后范围结束行，源文件名称，源文件范围开始行，源文件范围结束行)]
    """
    mappings = []
    line_number = 1  # 当前在 .i 文件中的行号
    current_source = ""  # 当前源文件
    current_line = 1  # 当前源文件的起始行

    with open(file_path, "r") as f:
        for line in f:
            # 匹配预处理指令：例如：# 10 "source.c"
            match = re.match(r'^# (\d+) "(.+?)"', line)
            if match:
                new_line = int(match.group(1))
                source_file = match.group(2)

                # 如果我们已经跟踪了一个源文件，则记录它的范围
                if current_source and new_line < line_number:
                    mappings.append(
                        (
                            start_line,
                            line_number - 1,
                            source_file,
                            current_line,
                            current_line + (line_number - start_line) - 1,
                        )
                    )

                # 更新为新的源文件或位置
                start_line = line_number
                current_source = source_file
                current_line = new_line
            line_number += 1

        # 添加最后一条记录
        if current_source:
            mappings.append(
                (
                    start_line,
                    line_number - 1,
                    source_file,
                    current_line,
                    current_line + (line_number - start_line) - 1,
                )
            )

    return mappings


def remove_preprocessor_markers(input_path, output_path=None):
    """
    去除 .i 预处理文件中所有的 # line "file" 标记，但保留该行其他内容。

    参数:
        input_path (str): 输入的 .i 文件路径。
        output_path (str, optional): 输出文件路径。如果为 None，则返回字符串内容。

    返回:
        str | None: 如果 output_path 为 None，则返回清理后的文本；
                    否则写入文件并返回 None。
    """
    # line_directive_pattern = re.compile(r'^#\s+\d+\s+".*"$')

    cleaned_lines = []

    with open(input_path, "r") as f:
        for line in f:
            # 如果是预处理标记行，则移除标记部分，保留空行
            if line.startswith("#"):
                cleaned_lines.append("")  # 保留一行空行
            else:
                cleaned_lines.append(line.rstrip("\n"))  # 去掉换行符便于统一处理

    cleaned_content = "\n".join(cleaned_lines)

    if output_path is None:
        return cleaned_content
    else:
        with open(output_path, "w") as f:
            f.write(cleaned_content)
        return None


removed_markers = remove_preprocessor_markers("demo.cpp.i", "demo.cpp.i.cleaned")

mappings = parse_preprocessor_file("demo.cpp.i.cleaned")
print(mappings)
# sys.exit()
fm = FileManager(".")
file = fm.get_abspath("demo.cpp.i.cleaned")
cmd = f"""

docker cp {file} cfg_extractor:/root/app/demo.cpp
docker exec -it cfg_extractor /root/app/checker  /root/app/demo.cpp -o /root/app/out.json
docker cp cfg_extractor:/root/app/out.json .

"""
# docker run --rm -itd --name cfg_extractor registry.cn-wulanchabu.aliyuncs.com/sky-group/cfg-extractor-json:v0.1 bash
# docker stop cfg_extractor
# Execute the cmd using subprocess
subprocess.run(cmd, shell=True)

cfg_manager = CFGManager("out.json")

mapping = cfg_manager._locate_line_col_to_node((45, 12))
print(mapping)
tu = parse_file("demo.cpp.i")
file_cursor = tu.cursor
func_decl = get_func_decl(file_cursor, "worker")
print(func_decl.spelling, func_decl.location)
# print(next(func_decl.get_tokens()).location)
start_pos = get_node_start_pos(func_decl)
if start_pos is None:
    raise Exception("Cannot find start pos")
cfg_id = cfg_manager._find_function_cfg_index(*start_pos)
print(func_decl.location.line, cfg_id)

# for cu in recursive_walk_non_control_flow_nodes(func_decl):
#     node_id = cfg_manager._locate_cursor_to_cfg_node(cfg_id, cu)
#     print(cu.location.line, cu.location.column, cu.kind, node_id)
new_cfg = cfg_manager.construct_cfg_with_stmts(cfg_id, func_decl)
cfg_repr = cfg_manager.format_label(new_cfg)
fm.dot_dump(cfg_repr, "cfg_" + func_decl.spelling + ".dot")

lock_exprs: List[Tuple[int, int]] = []
unlock_exprs: List[Tuple[int, int]] = []
for node_id in new_cfg.nodes:
    for stmt_index, stmt in enumerate(new_cfg.statements(node_id)):
        locks = (
            iter_ast(stmt)
            .filter(
                lambda cu: cu.kind == CursorKind.CALL_EXPR
                and cu.spelling in ("pthread_mutex_lock",)
            )
            .l
        )
        if len(locks) > 0:
            lock_exprs.append((node_id, stmt_index))
        locks = (
            iter_ast(stmt)
            .filter(
                lambda cu: cu.kind == CursorKind.CALL_EXPR
                and cu.spelling in ("pthread_mutex_unlock",)
            )
            .l
        )
        if len(locks) > 0:
            unlock_exprs.append((node_id, stmt_index))
            print(locks, node_id)
print(lock_exprs, unlock_exprs)
import networkx as nx


class Path(TypedDict):
    lock_node_id: int
    lock_expr_index: int
    unlock_node_id: int
    unlock_expr_index: int
    path: List[int]


paths: List[Path] = []

for lock_expr in lock_exprs:
    for unlock_expr in unlock_exprs:
        for p in nx.all_simple_paths(new_cfg, lock_expr[0], unlock_expr[0]):
            paths.append(
                {
                    "lock_expr_index": lock_expr[1],
                    "unlock_expr_index": unlock_expr[1],
                    "lock_node_id": lock_expr[0],
                    "unlock_node_id": unlock_expr[0],
                    "path": p,
                }
            )
print(paths)
# 遍历每一条路径，对于起始节点，列出stmt_index后面的所有语句；
# 对于结束节点，列出stmt_index前面的所有语句
# 对于中间节点，列出全部语句
for path in paths:
    print("Traversing path:", path)
    for i, node_id in enumerate(path["path"]):
        # 获取当前节点的语句列表
        statements = new_cfg.statements(node_id)

        if i == 0:  # 起始节点
            # 列出 stmt_index 后面的所有语句
            start_stmt_index = path["lock_expr_index"]
            print(f"Start Node ({node_id}): Statements after index {start_stmt_index}:")
            for stmt in statements[start_stmt_index:]:
                print(stmt.kind, stmt.location.line)
        elif i == len(path["path"]) - 1:  # 结束节点
            # 列出 stmt_index 前面的所有语句
            end_stmt_index = path["unlock_expr_index"]
            print(f"End Node ({node_id}): Statements before index {end_stmt_index}:")
            for stmt in statements[:end_stmt_index]:
                print(stmt.kind, stmt.location.line)
        else:  # 中间节点
            # 列出全部语句
            print(f"Intermediate Node ({node_id}): All statements:")
            for stmt in statements:
                print(stmt.kind, stmt.location.line)
